
function ServerWar() {
    this.count = 0;                 // 跨服战当前进行的次数
    // 晋级赛战斗回合
    this.progress = 'register';     // 跨服战周期内的进度
    this.round = 0;                 // 跨服战周期内每级赛事的回合数

    this.players = {                // 所有报名的玩家Player对象
        //uid : Player({ _id:uid, info:, status:{}, role:{}, team:{}})
    };

    this.registers = {};            // 已报名玩家{UID : 1} Map

    this.top32 = [];                // 32强玩家的位置和UID信息
    this.top32Users = {
        /* uid: {                   // 玩家UID
            rank:0,                 // 排名等级[32, 16, 8, 4, 2, 1],多少强玩家
            worth: 0,               // 身价
            save_team:0,            // (0/1) 是否已保存阵型
        }*/ 
    };

    this.topOneSelects = [];        // 晋级赛每轮的参赛者的UID数组(eg:16强的参赛者为所有的32强玩家)
    this.topOneResults = {          // 晋级赛每轮战斗结果
        /*
        1:[],                       // 元素为0/1的数组，数组长为topOneSelects.length, 1表示对应玩家胜利 
        2:[],                       
        3:[],                       
        4:[],
        5:[],
        */
    },

    this.replayId = 0;              // 回放计数
    this.replays = {                // 淘汰赛战报回放( 32_1 16_1 : [def_name,def_uid,succ,replayId])
        // uid : {}
    };

    this.topOneReplayMap = {        // 32强回放映射表
        /*'16_1' : {
           replayId:winnerUid,    
        }*/  
    };

    this.topOneReplays = {          // 前32强所有回放缓存
        //replayId: replay,         // replay为对应回放的JSON字符串格式
    };

    this.open = 1;                  // 0/1 跨服战是否正常开启
    this.fighting = false;          // 是否正在战斗计算
    this.updates = {};
}

ServerWar.create = function() {

    var serverWar = {
        _id: 'server_war', 
        replay_id:0,
        user_replay:{},
        top_replay: {},
        top_result: {},
        progress: 'open',
        round: 0,
        registers: {},
        top32: [],
        top32_users: {},
        topones: [],
        count : 0,
    };

    gDBWorld.insert(serverWar, function(err, result){});
};

ServerWar.prototype = {
    init : function(callback) {
        gDBWorld.findOne({_id : 'server_war'}, {}, function(err, doc){
            if( doc ) {
                this.replayId = doc.replay_id;
                this.replays = doc.user_replay;
                this.topOneReplayMap = doc.top_replay;
                this.topOneResults = doc.top_result;
                this.progress = doc.progress;
                this.round = doc.round;
                this.registers = doc.registers;
                this.top32 = doc.top32;
                this.top32Users = doc.top32_users;
                this.topOneSelects = doc.topones;
                this.count = doc.count;

                this._init(callback);
            }else{
                callback && callback(false);
            }
        }.bind(this));
    },

    _init : function(callback) {
        if( this.progress != 'registers' && this.progress != 'open' ) {
            // 读取32强玩家数据
            for( var i=0,len=this.top32.length; i<len; i++ ) {
                this.top32[i] = +this.top32[i];
            }

            var cursor = gDBUser.find({_id:{"$in":this.top32}});
            cursor.each(function(err, item){
                if( err ) {
                    ERROR(err);
                    return;
                }
                if( item ) {
                    var uid = item._id;
                    var player = new Player(uid);
                    player.initWithUniverseData(item);
                    this.players[uid] = player;
                }

                if( cursor.isClosed() ) {
                    callback && callback(true);
                }
            }.bind(this));
        }else{
            callback && callback(true);
        }
    },

    addUpdate : function(key, value) {
        this.updates[key] = value;
    },

    save : function(callback) {
        if( Object.keys(this.updates).length == 0 ) {
            callback && callback(true);
            return;
        }
        
        var updates = this.updates;
        gDBWorld.update({_id : 'server_war'}, {$set:updates}, function(err, result){
            if( err ) {
                ERROR(err);
                ERROR(updates);
                callback && callback(false);
            }else {
                callback && callback(true);
            }
        });

        this.updates = {};
    },

    saveServerWar : function() {
        var serverWar = {
            _id: 'server_war', 
            replay_id: this.replayId,
            top_replay: this.topOneReplayMap,
            top_result: this.topOneResults,
            progress: this.progress,
            round: this.round,
            registers: this.registers,
            user_replay: this.replays,
            top32: this.top32,
            top32_users: this.top32Users,
            topones: this.topOneSelects,
            count : this.count,
        };
        gDBWorld.save(serverWar, function(err, result){
            if( err ) {
                ERROR(err);
            }
        });
    },

    fight : function(playerUid, enemyUid) {
        var player = this.players[playerUid];
        var enemy = this.players[enemyUid];
        
        var playerFighter = new Fighter();
        playerFighter.initWithPlayer(player, false, true);

        var enemyFighter = new Fighter();
        enemyFighter.initWithPlayer(enemy, false, true);

        var battleReport = playerFighter.fight(enemyFighter);

        return battleReport;
    },

    saveReplay : function(battleReport) {
        this.replayId += 1;
        var replayId = 'replay_' + this.replayId;
        gDBReplay.save({_id:replayId, replay:JSON.stringify(battleReport)}, function(err, result){}); 
        
        return this.replayId;
    },

    addUserReplay : function(uid, enemy, progress, round, succ, replayId) {
        // 添加用户自己的战报映射
        var replays = this.replays[uid];
        if( !replays ) {
            replays = {};
            this.replays[uid] = replays;
        }

        replays[util.format('%s_%s', progress, round)] = [enemy.user.info.un, enemy.uid, succ, replayId];
    },

    addTopOneReplay : function(replayKey, winnerUid, replayId, battleReport){
        // 添加晋级赛战报的映射
        this.topOneReplayMap[replayKey][replayId] = winnerUid;
        // 添加晋级赛战报缓存
        this.topOneReplays[replayId] = JSON.stringify(battleReport); 
    },

    generateFighters : function(fighters, round, num) {
        // 匹配战斗,淘汰部分玩家,挑选出num/一半个胜利的玩家,返回剩余

        var fighterCount = 0;
        fighters.forEach(function(fighterUid){
            // 有效的玩家Id(0 表示已经输了出局)
            if( fighterUid ) fighterCount += 1;
        });

        num = num || Math.ceil(fighterCount/2);          
        var fightMaxCount = fighterCount - num;
        var fightCount = 0;

        var cursor = 0;
        var maxCursor = fighters.length;
        var leftPos = -1;
        var rightPos = -1;
        var fighterUid = 0;

        for( ; cursor < maxCursor; cursor ++ ) {
            fighterUid = fighters[cursor];
            if( !fighterUid ) continue;

            if( leftPos < 0 ) {
                leftPos = cursor;
                continue;
            }

            if( rightPos < 0 ) {
                // 找到左右对手
                rightPos = cursor;

                var leftPlayer = this.players[fighters[leftPos]];
                var rightPlayer = this.players[fighters[rightPos]];
                if (!leftPlayer) {
                    fightCount += 1;
                    fighters[leftPos] = 0;
                } else if (!rightPlayer) {
                    fightCount += 1;
                    fighters[rightPos] = 0;
                } else {
                    var battleReport = this.fight(fighters[leftPos], fighters[rightPos]);
                    var failPos = battleReport.success ? rightPos : leftPos;
                    var winPos = battleReport.success ? leftPos : rightPos;
                    var failUid = fighters[failPos];
                    var replayId = this.saveReplay(battleReport);
                    this.addUserReplay(failUid, this.players[fighters[winPos]], 32, round, 0, replayId);
                    
                    fightCount += 1;
                    fighters[failPos] = 0;  // 输了淘汰掉
                }

                leftPos = -1;
                rightPos = -1;

                if( fightCount >= fightMaxCount ) {
                    break;
                }
            }
        }
        
        return fighterCount - fightCount;
    },

    prepareTop32 : function(callback) {
        // 读取玩家数据,准备Player
        var cursor = gDBUser.find();
        cursor.each(function(err, item){
            if( err ) {
                ERROR(err);
                return;
            }
            if( item ) {
                var uid = item._id;
                var player = new Player(uid);
                player.initWithUniverseData(item);
                this.players[uid] = player;
            }

            if( cursor.isClosed() ) {
                callback && callback();
            }
        }.bind(this));
    },

    fightTop32 : function() {
        // 计算淘汰赛,每轮选8个出来
        
        this.replayId = 0;
        this.replays = {};

        var wins = [];
        
        this.replayId = 0;
        var registersCounter = Object.keys(this.registers).length;
        if( registersCounter > 32 ) {
            // 报名人数大于 
            for( var round = 1; round <= 4; round++) {
                var fighters = Object.keys(this.registers);

                wins.forEach( function(winUid) {
                    winUid = winUid.toString();
                    fighters[fighters.indexOf(winUid)] = 0;
                });

                fighters = fighters.sort(function(a, b){ return (Math.random() > 0.5) ? 1 : -1;});
                var fighterNum = fighters.length - wins.length; 
                if( fighterNum > 16 ) {
                    while( (fighterNum = this.generateFighters(fighters, round)) > 16 ) {}
                }
                
                fighterNum = this.generateFighters(fighters, round, 8); 

                for( var i=0,max=fighters.length; i<max; i++ ) {
                    var fighterUid = +fighters[i];
                    if( fighterUid ) {
                        wins.push(fighterUid);
                    }
                }
            }

        }else if( registersCounter == 32 ) {
            wins = Object.keys(this.registers);
        }
        
        // 32强玩家位置随机分配
        wins = wins.sort(function(a, b){ return (Math.random() > 0.5) ? 1 : -1;});
        
        this.topOneSelects = wins;
        this.top32 = wins.slice();
        this.registers = {};

        for( var i=0; i<wins.length; i++ ) {
            this.top32Users[wins[i]] = {'rank':32, 'worth':0, 'save_team' :0};
        }
    },
    
    isPKWin : function(pos) {
        // 5打3胜

        var wins = 0;
        for(var i = 1; i <= 5; i++ ) {
            wins += this.topOneResults[i][pos] || 0; 
        }

        return wins >= 3;
    },

    fightTopOne : function(progress, round) {
        // 晋级赛 , 32->16 progress = 16
        var topOneResult = this.topOneResults[round];

        for( var i = 0, len = this.topOneSelects.length/2; i < len; i++ ) {
            
            var replayKey = util.format('%s_%d', progress, i+1)
            var replays = this.topOneReplayMap[replayKey];

            if( !replays ) {
                replays = {};
                this.topOneReplayMap[replayKey] = replays;
            }

            var playerPos = i*2;
            var enemyPos = i*2+1;

            var playerUid = this.topOneSelects[playerPos]; 
            if( this.isPKWin(playerPos) ) {
                topOneResult[playerPos] =  topOneResult[enemyPos] = 0;
                continue; 
            }

            var enemyUid = this.topOneSelects[enemyPos];
            if( this.isPKWin(enemyPos) ) {
                topOneResult[playerPos] =  topOneResult[enemyPos] = 0;
                continue; 
            }

            var battleReport = this.fight(playerUid, enemyUid);
            var winnerPos = battleReport.success ? playerPos : enemyPos;
            var winnerUid = this.topOneSelects[winnerPos];

            var failPos = battleReport.success ? enemyPos : playerPos;
            var failUid = this.topOneSelects[failPos];

            topOneResult[failPos] = 0;
            topOneResult[winnerPos] = 1;

            var replayId = this.saveReplay(battleReport);
            replays[replayId] = winnerUid;

            this.addUserReplay(failUid, this.players[winnerUid], progress, round, 0, replayId);
            this.addUserReplay(winnerUid, this.players[failUid], progress, round, 1, replayId);

            this.addTopOneReplay(replayKey, winnerUid, replayId, battleReport);

       } 

        // 晋级赛每一轮结束后,所有才参赛玩家均可以重新布阵
        for( var i=0, len = this.topOneSelects.length; i<len; i++ ) {
            this.top32Users[this.topOneSelects[i]].save_team = 0;
        }

        if( round == 5 ) {
            // 5轮结束之后才改写晋级排名
            for( var i = 0, len = this.topOneSelects.length; i < len; i++ ) {
                if( this.isPKWin(i) ) {
                    this.top32Users[this.topOneSelects[i]].rank = +progress;
                }
            } 
        }

    },

    tick : function() {
        if( this.fighting ) return;

        var progress = this.getProgress();

        // 第一次必须进入报名阶段
        if( this.progress == 'open' && progress.progress != 'register' ) return;

        if( progress.progress == 'replay' ) return;

        if( progress.progress == 'register' ) {
            if( this.progress != 'register' ) {
                this.progress = 'register';
                this.round = 1;
                
                this.replays = {};
                this.topOneSelects = [];
                this.top32 = [];
                this.topOneReplays = {};
                this.topOneReplayMap = {};
                this.top32Users = {};
                this.topOneResults = { 1:[], 2:[], 3:[], 4:[], 5:[] };

                this.saveServerWar();
            }
            return;
        }        
        
        if( this.progress == 'register' && Object.keys(this.registers).length < 32 ) {
            // 参战人数不够,战斗不能正常开启
            this.open = 0;
            return; 
        }
        
        this.open = 1;

        if( progress.progress == this.progress && progress.round == this.round ) return;
        
        this.fighting = true;
        // @todo: 遗漏的回合

        if( progress.progress != this.progress ) {
            // 进入下一阶段
            if( progress.progress == '32' ) {
                // 淘汰赛
                this.prepareTop32(function(){
                    this.fightTop32();
                    this.progress = progress.progress;
                    this.round = progress.round;

                    this.fighting = false;
                    this.count += 1;

                    this.saveServerWar();
                }.bind(this));
                return;
            }else{
                // 晋级赛, 准备战前数据
                if( progress.progress != '16' ) {
                    var topOneSelects = [];
                    for( var i=0,len=this.topOneSelects.length; i<len; i++ ) {
                        if( this.isPKWin(i) ) {
                            topOneSelects.push(this.topOneSelects[i]);
                        }
                    }
                    this.topOneSelects = topOneSelects;
                }else{
                    this.topOneSelects = this.top32.slice();
                }

                this.topOneResults = { 1:[], 2:[], 3:[], 4:[], 5:[] };
                this.fightTopOne(progress.progress, 1);
            }
        }else{
            // 进入下一回合
            if( progress.round != this.round ) {
                this.fightTopOne(this.progress, progress.round);
            }
        }

        this.progress = progress.progress;
        this.round = progress.round;

        this.fighting = false;

        this.saveServerWar();
    },

    getProgress : function() {
        // 根据时间获取战斗进度
        //return {'progress': 'replay', 'round': 0};
        //return {'progress': '1', 'round': 5};
        //return {'progress': 'register', 'round': 0};

        var nowDate = new Date();
        var nowWeekHour = nowDate.getDay() * 24 + nowDate.getHours() + nowDate.getMinutes() / 60;
        var allWeekHour = 7 * 24;

        var matchProgress = 'replay';
        var round = 0;

        for( var progress in gConfServerWarSchedule ) {
            var progressConf = gConfServerWarSchedule[progress];
            var startWeekHour = progressConf.StartWeek * 24 + progressConf.StartTime;
            
            var nowHour = nowWeekHour;
            if( nowHour < startWeekHour ) {
                nowHour += allWeekHour;
            }

            var passHour = nowHour - startWeekHour;
            if( passHour <= progressConf.KeepHour ) {
                matchProgress = progress;

                if( progress != 'register' && progress != '32') {
                    // 计算回合
                    round = Math.ceil(passHour/progressConf.KeepHour * 5);
                    round = round ? round : 1;
                }else if( progress == '32' ) {
                    round = 4; 
                }

                break;
            }
        }

        return {'progress':matchProgress, 'round':round};
    },

    register : function(uid, user){
        // 数据库特殊处理

        var serverWar = this;

        gDBUser.save(user, function(err, result){
            if( err ) {
                ERROR(err);
            }else {
                var key = 'registers.'+uid;
                var updates = {};
                updates[key] = 1;

                gDBWorld.update({_id : 'server_war'}, {$set:updates}, function(err, result){
                    if( err ) {
                        ERROR(err);
                    }else {
                        serverWar.registers[uid] = 1;
                    }
                });
            }
        });
    },

    saveTeam : function(user) {
        gDBUser.save(user, function(err, result){
            if( err ) {
                ERROR(err);
            }else {
                
                var uid = user._id;
                var player = new Player(uid);
                player.initWithUniverseData(user);
                this.players[uid] = player;
            }    
        }.bind(this));
    },

    getReplay : function(replayId, callback) {
        var topOneReplays = this.topOneReplays;  
        if( replayId in topOneReplays ) {
            callback && callback(topOneReplays[replayId]);
            return;
        }  
       
        var topOneReplayIds = {};
        var topOneReplayMap = this.topOneReplayMap;
        for( var id in topOneReplayMap ) {
            for( var rId in topOneReplayMap[id] ) {
                topOneReplayIds[rId] = 1; 
            }     
        }
        
        gDBReplay.findOne({_id : 'replay_'+replayId}, {}, function(err, doc){
            if( doc ) {
                var battleReport = doc.replay;
                if( replayId in topOneReplayIds ) {
                    this.topOneReplays[replayId] = battleReport; 
                }  

                callback && callback(battleReport);
            }else{
                callback && callback(null);
            }
        }.bind(this));
    },
}

exports.register = function(req, res, resp) {
    var uid = req.uid;
    do {
        // 时间限制
        if( gServerWar.getProgress().progress != 'register' ) {
            resp.code = 1; resp.desc = 'not regist time'; break;
        }
            
        if( gServerWar.registers[uid] ) {
            resp.code = 1; resp.desc = 'have registered'; break;
        }

        gServerWar.register(req.uid, req.args.user);

    }while(false);

    onReqHandled(res, resp);
}

exports.get = function(req, res, resp) {
    var uid = req.uid;
    do {
        var register = 0;
        var isSaveTeam = 0;
        
        if( !gServerWar.open ) {
            resp.data.open = 0; 
            break;
        }

        if( gServerWar.getProgress().progress == 'register' ) {
            if( (uid in gServerWar.registers) ) {
                register = 1; 
            }    

            resp.data.under_register = 1; 
        }else {
            var top32Users = gServerWar.top32Users;
            if( (uid in gServerWar.registers) || (uid in gServerWar.replays) 
                || (uid in top32Users) ) {
                register = 1; 
            }    

            if( top32Users[uid] && top32Users[uid].save_team ) {
                isSaveTeam = 1; 
            }

            if( !gServerWar.fighting ) {
                
                var players = gServerWar.players; 
                var topUsers = {};
                for( var id in top32Users ) {
                    var topPlayer = players[id];
                    if (!topPlayer) {
                        continue;
                    }

                    var user = topPlayer.user;
                    topUsers[id] = {
                        'name' : user.info.un,
                        'headpic' : user.info.headpic,
                        'level': user.status.level,
                        'rank' : top32Users[id].rank,
                        'fight_force' : user.status.fight_force,
                        'worth': top32Users[id].worth,
                    }; 
                }
                
                var nowProgress = gServerWar.progress;
                var reqCount = req.args.count;
                if( reqCount && (reqCount == gServerWar.count) 
                    && (nowProgress in gConfServerWarSupport) ) {

                    var supports = req.args.support;  
                    var supportUpdates = {};
                    for( var progress in supports ) {
                        var support = supports[progress]; 
                        if( support.got || support.succ ) {
                            continue; 
                        }
                        
                        var supportUser = gServerWar.top32Users[support.uid];
                        if (!supportUser || supportUser.rank > progress) {
                            continue;
                        }
                        
                        if( gServerWar.round == 5 && progress == nowProgress ) {
                            // 晋级赛中每个进度比赛完成之后且只有处于当前进度的支持才能获取奖励
                            supportUpdates[progress] = 1;
                        }else {
                            // 获取胜利但错失领奖
                            supportUpdates[progress] = 0;
                        }
                    }

                    if( Object.keys(supportUpdates).length > 0 ) {
                        resp.data.support_updates = supportUpdates;
                    } 
                }

                resp.data.top_uids = gServerWar.top32;
                resp.data.top_users = topUsers; 
            }
        }

        resp.data.register = register;
        resp.data.save_team = isSaveTeam;
        resp.data.count = gServerWar.count;
    }while(false);

    onReqHandled(res, resp);
}

exports.save_team = function(req, res, resp) {
    var uid = req.uid;
    do {
        var saveTeamUnderRegister = req.args.save_team;
        if( gServerWar.getProgress().progress == 'register' ) {
            if( saveTeamUnderRegister ) {
                resp.code = 1; resp.desc = 'have saved under register'; break;
            }

            resp.data.under_register = 1; 
        }else {
            var top32Users = gServerWar.top32Users;
            if( !(uid in top32Users) ) {
                resp.code = 1; resp.desc = 'not in top32'; break;
            } 

            if( top32Users[uid].save_team ) {
                resp.code = 1; resp.desc = 'have saved under top32'; break;
            }
            
            top32Users[uid].save_team = 1;
            gServerWar.addUpdate(util.format('top32_users.%d.save_team',uid), 1);
        }

        gServerWar.saveTeam(req.args.user);

    }while(false);

    gServerWar.save();
    onReqHandled(res, resp);
}

exports.get_my_replay = function(req, res, resp) {
    var uid = req.uid;
    do {
        if( !((uid in gServerWar.replays) || (uid in gServerWar.top32Users)) ) {
            resp.code = 1; resp.desc = 'not register'; break;
        } 
        
        var replays = gServerWar.replays[uid];
        if( !replays ) {
            replays = {}; 
        }

        resp.data.replays = replays; 

    }while(false);

    onReqHandled(res, resp);
}

exports.get_replay_list = function(req, res, resp) {
    var uid = req.uid;
    do {
        // 没有战报时不报错
        var replays = gServerWar.topOneReplayMap[req.args.id];
        if( !replays ) {
            replays = {}; 
        }
        
        resp.data.replays = replays;
    }while(false);

    onReqHandled(res, resp);
}

exports.get_replay = function(req, res, resp) {
    var uid = req.uid;

    gServerWar.getReplay(req.args.id, function(battleReport) {
        if( !battleReport ) {
            resp.code = 1; 
            resp.desc = 'id error';
            onReqHandled(res, resp);
            return;
        } 

        resp.data.battle = battleReport;
        onReqHandled(res, resp);
    });
}

exports.support = function(req, res, resp) {
    var uid = req.uid;
    do {
        var supports = req.args.support;
        var supportUid = parseInt(req.args.id);
        var supportProgress = parseInt(req.args.progress);

        var top32Users = gServerWar.top32Users;
        if( !(supportUid in top32Users) ) {
            resp.code = 1; resp.desc = 'invalid id'; break;
        }
        
        var progress = gServerWar.progress;
        if( progress == 'register' ) {
            resp.code = 1; resp.desc = 'under register'; break;
        }

        if( supportProgress != progress/2 ) {
            resp.code = 1; resp.desc = 'not in support time'; break;
        }
        
        var supportUser = top32Users[supportUid];
        supportUser.worth += gConfLevel[req.args.level].ServerWarSupport 
            * gConfServerWarSupport[supportProgress].Gold;
        
        gServerWar.addUpdate(util.format('top32_users.%d.worth', supportUid), 
            supportUser.worth );
        
        resp.data.count = gServerWar.count;
    }while(false);

    gServerWar.save();
    onReqHandled(res, resp);
}

exports.get_reward = function(req, res, resp) {
    var uid = req.uid;
    do {
        if( !(uid in gServerWar.top32Users) ) {
            resp.code = 1; resp.desc = 'no reward'; break;
        }        
        
        if( !(gServerWar.progress == '1' && gServerWar.round == 5) ) {
            resp.code = 1; resp.desc = 'not got time'; break;
        }

        var userRank = +gServerWar.top32Users[uid].rank;
        if( userRank > 4 || !userRank ) {
            userRank = 32;
        }else if( userRank > 1) {
            userRank = 4; 
        }else {
            userRank = 1;
        }

        var rewardConf = gConfServerWarReward[userRank];
        if( !rewardConf ) {
            resp.code = 1; resp.desc = 'rank error '; break;
        }

        resp.data.awards = [rewardConf.Award1];  
    }while(false);

    onReqHandled(res, resp);
}

exports.ServerWar = ServerWar;
